using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

using Entities = PastCrew.Games.Chess.Engine.Entities;

namespace PastCrew.Games.Chess.Engine.Entities
{
    /// <summary>
    /// Representation of the Chess Board. The SetupBoard method will place the appropriate pieces in the required
    /// places. It will also set the properties to their default values. This class contains an inner class that represents a
    /// board square. A reference to a Square can be obtained by calling the Board GetSquare() method, passing [char][int].
    /// There is also an inner class Position that keeps track of the [Letter][Number] and [X][Y] locations of each square.
    /// </summary>
    public class Board
    {
        #region private member variables

        private static Square[,] _board;
        private static List<Entities.Pieces.BasePiece> _whitePieces;
        private static List<Entities.Pieces.BasePiece> _blackPieces;
        
        #endregion

        #region constructors
                
        private Board()
        {            
        }

        #endregion

        #region public methods

        /// <summary>
        /// This method will (re)set the Board to its initial state.
        /// </summary>
        public static void SetupBoard()
        {
            #region reset statics for Unit Tests

            _board = new Square[8, 8];
            _whitePieces = new List<Entities.Pieces.BasePiece>(16);
            _blackPieces = new List<Entities.Pieces.BasePiece>(16);

            #endregion

            #region initialize squares

            bool colorFlip = true;

            for (int i = 0; i <= 7; i++)
            {
                colorFlip = !colorFlip;
                for (int j = 0; j <= 7; j++)
                {
                    _board[j, i] = new Square();
                    _board[j, i].Position = new Position(Convert.ToChar((i + 1) + 64), (j + 1));
                    if (colorFlip)
                        _board[j, i].SquareColor = Color.white;
                    else
                        _board[j, i].SquareColor = Color.black;

                    colorFlip = !colorFlip;
                }
            }

            #endregion

            #region setup white pieces

            Entities.Pieces.BasePiece whiteRookL = new Entities.Pieces.Rook(Color.white);
            Entities.Pieces.BasePiece whiteRookR = new Entities.Pieces.Rook(Color.white);
            Entities.Pieces.BasePiece whiteKnightL = new Entities.Pieces.Knight(Color.white);
            Entities.Pieces.BasePiece whiteKnightR = new Entities.Pieces.Knight(Color.white);
            Entities.Pieces.BasePiece whiteBishopL = new Entities.Pieces.Bishop(Color.white);
            Entities.Pieces.BasePiece whiteBishopR = new Entities.Pieces.Bishop(Color.white);
            Entities.Pieces.BasePiece whiteQueen = new Entities.Pieces.Queen(Color.white);
            Entities.Pieces.BasePiece whiteKing = new Entities.Pieces.King(Color.white);

            _board[0, 0].Occupant = whiteRookL;
            _board[0, 1].Occupant = whiteKnightL;
            _board[0, 2].Occupant = whiteBishopL;
            _board[0, 3].Occupant = whiteQueen;
            _board[0, 4].Occupant = whiteKing;
            _board[0, 5].Occupant = whiteBishopR;
            _board[0, 6].Occupant = whiteKnightR;
            _board[0, 7].Occupant = whiteRookR;

            _whitePieces.Add(whiteRookL);
            _whitePieces.Add(whiteRookR);
            _whitePieces.Add(whiteKnightL);
            _whitePieces.Add(whiteKnightR);
            _whitePieces.Add(whiteBishopL);
            _whitePieces.Add(whiteBishopR);
            _whitePieces.Add(whiteQueen);
            _whitePieces.Add(whiteKing);

            for (int i = 0; i <= 7; i++)
            {
                Entities.Pieces.BasePiece pawn = new Entities.Pieces.Pawn(Color.white);
                _board[1, i].Occupant = pawn;
                _whitePieces.Add(pawn);
            }

            #endregion

            #region setup black pieces

            Entities.Pieces.BasePiece blackRookL = new Entities.Pieces.Rook(Color.black);
            Entities.Pieces.BasePiece blackRookR = new Entities.Pieces.Rook(Color.black);
            Entities.Pieces.BasePiece blackKnightL = new Entities.Pieces.Knight(Color.black);
            Entities.Pieces.BasePiece blackKnightR = new Entities.Pieces.Knight(Color.black);
            Entities.Pieces.BasePiece blackBishopL = new Entities.Pieces.Bishop(Color.black);
            Entities.Pieces.BasePiece blackBishopR = new Entities.Pieces.Bishop(Color.black);
            Entities.Pieces.BasePiece blackQueen = new Entities.Pieces.Queen(Color.black);
            Entities.Pieces.BasePiece blackKing = new Entities.Pieces.King(Color.black);

            _board[7, 0].Occupant = blackRookL;
            _board[7, 1].Occupant = blackKnightL;
            _board[7, 2].Occupant = blackBishopL;
            _board[7, 3].Occupant = blackQueen;
            _board[7, 4].Occupant = blackKing;
            _board[7, 5].Occupant = blackBishopR;
            _board[7, 6].Occupant = blackKnightR;
            _board[7, 7].Occupant = blackRookR;

            _blackPieces.Add(blackRookL);
            _blackPieces.Add(blackRookR);
            _blackPieces.Add(blackKnightL);
            _blackPieces.Add(blackKnightR);
            _blackPieces.Add(blackBishopL);
            _blackPieces.Add(blackBishopR);
            _blackPieces.Add(blackQueen);
            _blackPieces.Add(blackKing);

            for (int i = 0; i <= 7; i++)
            {
                Entities.Pieces.BasePiece pawn = new Entities.Pieces.Pawn(Color.black);
                _board[6, i].Occupant = pawn;
                _blackPieces.Add(pawn);
            }

            #endregion

            #region reset properties

            for (int y = 0; y <= 7; y++)
            {
                for (int x = 0; x <= 7; x++)
                {
                    if (_board[y, x].Occupied)
                        _board[y, x].Occupant.HasMoved = false;
                }
            }

            #endregion
        }

        /// <summary>
        /// Get a specific Square reference for a given postion on the board
        /// </summary>
        /// <param name="position">Position Instance referencing a specific location on the Board</param>
        /// <returns>Reference to the Square at position</returns>
        public static Square GetSquare(Position position)
        {
            return _board[position.Y, position.X];
        }

        /// <summary>
        /// Get a specific Square reference for a given X and Y coordinate on the board
        /// </summary>
        /// <param name="x">Represents the horizontal</param>
        /// <param name="y">Represents the vertical</param>
        /// <returns>Reference to the Square at position [x] [y]</returns>
        /// <remarks>The multi-dimensional array stores piece locactions [y,x]</remarks>
        public static Square GetSquare(int x, int y)
        {
            if (x >= 0 && x <= 7)
            {
                if (y >= 0 && y <= 7)
                {
                    return _board[y, x];
                }
                throw new ArgumentException(Properties.Resources.ex_illegalYCoordinateValue, "Y");
            }
            throw new ArgumentException(Properties.Resources.ex_illegalXCoordinateValue, "X");
        }

        /// <summary>
        /// Moves a Piece to a new position
        /// </summary>
        /// <param name="piece">Piece</param>
        /// <param name="newPosition">Position</param>
        /// <returns></returns>
        public static bool MovePiece(Entities.Pieces.BasePiece piece, Position newPosition)
        {
            // See if move is legal
            if(Entities.MoveResult.invalid == piece.ValidateMove(newPosition) )
                return false;                       

            Square originate = piece.CurrentSquare;
            Square target = GetSquare(newPosition);
            originate.Occupant = null;
            target.Occupant = piece;
            
            return true;
        }

        #endregion

        #region inner

        public class Position
        {
            private char _horizontal;
            private int _vertical;
            private int _x;
            private int _y;

            private Regex _legalHorizontal = new Regex("[A-Ha-h]");
            private Regex _legalVertical = new Regex("[1-8]");

            public Position(char horizontal, int vertical)
            {
                this.Horizontal = horizontal;
                this.Vertical = vertical;
            }

            public Position(int X, int Y)
            {
                this.X = X;
                this.Y = Y;
            }

            public static int HorizontalToInt32(char horizontal)
            {
                int horizontalIndex = Convert.ToInt32(horizontal);
                horizontalIndex = horizontalIndex > 90 ? (horizontalIndex - 97) : (horizontalIndex - 65);

                return horizontalIndex;
            }

            public static char Int32ToHorizontal(int x)
            {
                return Convert.ToChar(x + 65);
            }
            
            public char Horizontal
            {
                get { return _horizontal; }
                set
                {
                    if (!_legalHorizontal.Match(value.ToString()).Success)
                        throw new ArgumentException(Properties.Resources.ex_illegalHorizontalValue, "horizontal");
                    _horizontal = value;
                    _x = HorizontalToInt32(value);

                }
            }
            
            public int Vertical
            {
                get { return _vertical; }
                set
                {
                    if (!_legalVertical.Match(value.ToString()).Success)
                        throw new ArgumentException(Properties.Resources.ex_illegalVerticalValue, "vertical");
                    _vertical = value;
                    _y = (value - 1);
                }
            }

            public int X
            {
                get { return _x; }
                set
                {
                    if (value >= 0 && value <= 7)
                    {
                        _x = value;
                        _horizontal = Int32ToHorizontal(value);
                    }
                    else
                    {
                        throw new ArgumentException(Properties.Resources.ex_illegalXCoordinateValue, "X");
                    }
                }
            }

            public int Y
            {
                get { return _y; }
                set
                {
                    if (value >= 0 && value <= 7)
                    {
                        _y = value;
                        _vertical = (value + 1);
                    }
                    else
                    {
                        throw new ArgumentException(Properties.Resources.ex_illegalYCoordinateValue, "Y");
                    }
                }
            }
        }

        public class Square
        {
            private Entities.Color _squareColor;
            private bool _occupied;
            private Entities.Pieces.BasePiece _occupant;
            private Position _position;

            public Position Position
            {
                get { return _position; }
                set { _position = value; }
            }

            public Entities.Pieces.BasePiece Occupant
            {
                get { return _occupant; }
                set
                {
                    _occupant = value;
                    if (value != null)
                    {
                        this._occupied = true;
                        _occupant.CurrentSquare = this;
                    }
                    else
                    {
                        // Set Occupied to false is Occupant is set to null
                        this._occupied = false;
                    }
                }
            }

            public bool Occupied
            {
                get { return _occupied; }
                set 
                {
                    // Set Occupant to null if Occupied is set to false
                    if (value == false)
                        this._occupant = null;
                    _occupied = value;
                }
            }

            public Entities.Color SquareColor
            {
                get { return _squareColor; }
                set { _squareColor = value; }
            }

            public override int GetHashCode()
            {
                return (this.Position.X * 10) + this.Position.Y;
            }

            /// <summary>
            /// Checks to see if square is threatened for a given color.
            /// </summary>
            /// <param name="color">which color would be threatened</param>
            /// <returns>List<Entities.Pieces.BasePiece></returns>
            public List<Entities.Pieces.BasePiece> GetThreatenedByList(Entities.Color color)
            {
                bool dummyNeeded = !this.Occupied;
                if (dummyNeeded)
                {
                    // Spawn pawn
                    Engine.Entities.Pieces.Pawn pawn = new Engine.Entities.Pieces.Pawn();
                    pawn.Color = color != Color.white ? Color.black : Color.white;
                    this.Occupant = pawn;
                }

                List<Entities.Pieces.BasePiece> piecesThatThreaten = new List<PastCrew.Games.Chess.Engine.Entities.Pieces.BasePiece>();
                if (color == Color.white)
                {
                    foreach (Entities.Pieces.BasePiece piece in _blackPieces)
                    {
                        List<Entities.SquareStatus> validMoves = piece.GetValidMoves();
                        foreach (Entities.SquareStatus squareStatus in validMoves)
                        {
                            if (squareStatus.Square.GetHashCode() == this.GetHashCode())
                            {
                                piecesThatThreaten.Add(piece);
                            }
                        }
                    }
                }
                else
                {
                    foreach (Entities.Pieces.BasePiece piece in _whitePieces)
                    {
                        List<Entities.SquareStatus> validMoves = piece.GetValidMoves();
                        foreach (Entities.SquareStatus squareStatus in validMoves)
                        {
                            if (squareStatus.Square.GetHashCode() == this.GetHashCode())
                            {
                                piecesThatThreaten.Add(piece);
                            }
                        }
                    }
                }

                if (dummyNeeded)
                {
                    this.Occupied = false;
                }
                return piecesThatThreaten;
            }
        }
        #endregion
    }
}
